<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { loadAMap } from "./utils/mapLoader";
import { formatPlugins } from "./utils/helpers";
import { PluginName, MapBaseOptions, AmapModulesOptions, AmapModuleEvent, ModuleLoadState, LoadedModule } from "./types";
// 使用 import.meta.glob 导入所有模块
const moduleFiles = import.meta.glob("./modules/*/*.ts");

// 定义组件名称
defineOptions({
  name: "AmapUI"
});

/**
 * 组件属性定义
 */
export interface Props {
  // 地图插件配置
  plugins?: PluginName[];
  // 地图基础配置选项
  opts?: MapBaseOptions;
  // 要加载的地图模块
  modules?: AmapModulesOptions[];
}

// 属性默认值
const props = withDefaults(defineProps<Props>(), {
  plugins: () => ["Scale", "ToolBar"],
  opts: () => ({
    zoom: 12,
    center: [116.397428, 39.90923]
  }),
  modules: () => []
});

// 地图实例引用
const map = ref<any>(null);
// 已加载的地图模块列表
const mapModules = ref<(LoadedModule | null)[]>([]);
// AMap全局对象引用
let AMap: any = null;

/**
 * 格式化插件名称 - 添加AMap.前缀
 */
const formattedPlugins = computed(() => formatPlugins(props.plugins));

/**
 * 是否显示搜索框 - 当加载Geocoder模块时显示
 */
const isShowSearch = computed(() => !!props.modules.find(item => item.name === "Geocoder"));

/**
 * 显示搜索框时的输入值 - 仅Geocoder模块使用
 */
const inputValue = ref("");

/**
 * 初始化地图
 */
const initMap = () => {
  map.value = new AMap.Map("container", {
    ...props.opts
  });

  // 动态加载所有格式化后的插件
  AMap.plugin(formattedPlugins.value, () => {
    props.plugins.forEach(plugin => {
      map.value.addControl(new AMap[plugin]());
    });
  });
};

// 模块加载缓存（单例模式）
const moduleCache = new Map<string, ModuleLoadState>();

/**
 * 动态加载单个模块的工厂函数
 * @param moduleName - 模块名称
 * @returns 模块实例
 */
const loadModuleFactory = async (moduleName: string) => {
  // 检查缓存
  if (moduleCache.has(moduleName)) {
    const cached = moduleCache.get(moduleName);
    if (cached?.loading) return cached.promise;
    if (cached?.instance) return cached.instance;
  }

  // 创建加载状态
  const loadState: ModuleLoadState = {
    promise: null, // 模块加载的Promise对象, 用于共享同一个加载请求，避免重复加载
    instance: null, // 模块加载完成后的实例, 用于缓存已加载的模块，供后续请求直接使用
    loading: true // 是否正在加载中
  };
  moduleCache.set(moduleName, loadState);
  console.log(moduleCache, "moduleCache");

  try {
    // 动态导入模块（处理不同扩展名）
    let modulePath = `./modules/${moduleName}/index.ts`;

    // 检查模块是否存在
    if (!moduleFiles[modulePath]) {
      throw new Error(`Module ${moduleName} not found`);
    }
    // 将加载Promise赋值给loadState.promise
    loadState.promise = (async () => {
      // 加载模块
      const module: any = await moduleFiles[modulePath]();
      const instance = module.default || module;

      // 注入AMap和map实例
      instance.AMap = AMap;
      instance.map = map.value;

      // 缓存实例
      loadState.instance = instance;
      return instance;
    })();
    // 等待加载完成并返回实例
    return await loadState.promise;
  } catch (error) {
    console.error(`Failed to load module ${moduleName}:`, error);
    throw new Error(`Failed to load ${moduleName} module. Please check if the module exists and has the correct extension.`);
  } finally {
    loadState.loading = false;
  }
};

/**
 * 动态加载模块
 * @returns 已加载的有效模块列表
 */
const loadModules = async (): Promise<(LoadedModule | null)[]> => {
  // 检查地图是否已初始化
  if (!map.value) {
    return [];
  }

  // 清理旧模块
  mapModules.value.forEach(module => {
    if (module?.moduleInstance?.events?.remove) {
      module?.moduleInstance.events.remove();
    }
  });
  mapModules.value = [];

  if (!props.modules.length) return [];

  // 并行加载指定的模块
  const moduleLoadingPromises = props.modules.map(async moduleConfig => {
    try {
      // 从工厂函数获取模块实例
      const moduleFactory = await loadModuleFactory(moduleConfig.name);
      // 声明并初始化initializedModule变量
      let moduleInstance: any = null;

      if (typeof moduleFactory.init === "function") {
        // 初始化模块
        moduleInstance = await moduleFactory.init(moduleConfig.markers, moduleConfig.extra || {});

        console.log(moduleInstance, "moduleInstance");

        // 绑定事件 - 适用于Marker、InfoWindow等单模块
        if (moduleConfig.events && Array.isArray(moduleInstance.instance) && moduleInstance.instance.length > 0) {
          moduleInstance.instance.forEach((item: any) => {
            executeModuleMethod(item, moduleConfig.events);
          });
        }

        // 为Geocoder模块添加地址变化事件监听;
        if (
          moduleConfig.name === "Geocoder" &&
          moduleInstance &&
          moduleInstance.events &&
          typeof moduleInstance.events.on === "function"
        ) {
          moduleInstance.events.on("addressChanged", (address: string) => {
            inputValue.value = address;
          });
        }
      }

      return { name: moduleConfig.name, moduleInstance, moduleFactory, moduleConfig } as LoadedModule;
    } catch (error) {
      console.error(`Module ${moduleConfig.name} initialization failed:`, error);
      return null;
    }
  });

  const modules = await Promise.all(moduleLoadingPromises);
  const validModules = modules.filter(Boolean);
  mapModules.value.push(...validModules);
  return validModules;
};

/**
 * 执行模块的事件绑定方法, 绑定到实例中模块方法，如Marker.on('click')
 * @param instance - 模块实例
 * @param events - 事件配置列表
 * @description 如果当你想要实现内置的事件之外，还想要在做些其他的操作的话，可以使用event
 */
const executeModuleMethod = (instance: any, events: Array<AmapModuleEvent> = []) => {
  if (!instance || typeof instance.on !== "function") {
    console.error("Invalid module instance provided");
    return;
  }

  events.forEach(event => {
    try {
      if (event && event.name && typeof event.cb === "function") {
        instance.on(event.name, (e: any) => {
          event.cb(e, event.name);
        });
      } else {
        console.warn("Invalid event configuration:", event);
      }
    } catch (error) {
      console.error(`Failed to bind event ${event?.name}:`, error);
    }
  });
};

/**
//TODO：绑定事件, 模块内手动添加on监听，与模块无关，纯实例的自定义事件，暂不开放
 * 
 * 执行模块的事件绑定方法, 绑定到实例方法，与内置模块无关，需要手动添加on监听
 * @param instance - 实例
 * @param events - 事件配置列表
 * @description 如果当你想要实现内置的事件之外，还想要在做些其他的操作的话，可以使用event
 */
// if (moduleConfig.events && moduleInstance && moduleInstance.events && typeof moduleInstance.events.on === "function") {
//   executeModuleEvents(moduleInstance, moduleConfig.events);
// }

// const executeModuleEvents = (instance: any, events: Array<AmapModuleEvent> = []) => {
//   console.log(instance, "instanceinstance");
//   events.forEach(event => {
//     try {
//       if (event && event.name && typeof event.cb === "function") {
//         console.log(222222, instance.events, event);
//         instance.events.on(event.name, (e: any) => {
//           event.cb(e, event.name);
//         });
//       } else {
//         console.warn("Invalid event configuration:", event);
//       }
//     } catch (error) {
//       console.error(`Failed to bind event ${event?.name}:`, error);
//     }
//   });
// };

/**
 * 获取模块实例
 * @param name - 模块名称
 * @returns 模块实例或undefined
 */
const getModule = (name: string): LoadedModule | null => {
  console.log(mapModules, "mapModules");
  return mapModules.value.find((m: any) => m.name === name) || null;
};

// 组件挂载时初始化
onMounted(async () => {
  AMap = await loadAMap(); // 复用全局实例
  // 因为只有initMap只有控件类插件，所以未做initMap未做Promise、await顺序处理
  initMap();
  await loadModules(); // 加载传入的模块
});

/**
 * 销毁清理方法
 */
const cleanup = () => {
  if (map.value) {
    map.value.destroy();
    map.value = null;
  }
};
// 组件卸载时清理
onBeforeUnmount(() => {
  cleanup();
});

// 暴露公共方法
defineExpose({
  loadModules,
  getModule,
  getMap: () => map.value,
  getAMap: () => AMap
});
</script>

<template>
  <div class="map-container">
    <!-- 地址搜索框 - 仅当加载Geocoder模块时显示 -->
    <template v-if="isShowSearch">
      <input type="text" class="address" v-model="inputValue" id="inputAddress" placeholder="请输入地址" />
      <div id="searchResult" class="search-result"></div>
    </template>

    <!-- 地图容器 -->
    <div id="container"></div>
  </div>
</template>

<style lang="scss" scoped>
.map-container {
  position: relative;
  width: 100%;
  height: 100%;
  padding: 0 !important;
  margin: 0 !important;
}
#container {
  width: 100%;
  height: 100%;
}
.address {
  position: absolute;
  top: 20px;
  left: 20px;
  z-index: 10;
  box-sizing: border-box;
  width: 50%;
  height: 30px;
  padding: 0 12px;
  margin-bottom: 10px;
  line-height: 30px;
  background-color: #ffffff;
  border: 1px solid #ececec;
  border-radius: 4px;
}
.search-result {
  position: absolute;
  top: 50px;
  left: 20px;
  z-index: 10;
  width: 50%;
  background-color: #ffffff;
}
</style>
